package com.fubowen.lepton.ui.reader.recycler

import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.style.LineBackgroundSpan
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.widget.TextView
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.graphics.component1
import androidx.core.graphics.component2
import androidx.core.graphics.component3
import androidx.core.graphics.component4
import androidx.core.graphics.toRect
import androidx.core.view.doOnPreDraw
import com.fubowen.lepton.R
import com.fubowen.lepton.extension.changeColor

class SelectionHelper(private val textView: TextView) {

    private var offsetY: Float = 0f
    private var isLongPress: Boolean = false
    private var spannable = SpannableStringBuilder()
    private var bgSpan = BgSpan()
    private var x: Float = 0f
    private var y: Float = 0f
    private var dragEndRectF: RectF = RectF()
    private var dragStartRectF: RectF = RectF()
    private var dragSelectionStart = false
    private var dragSelectionEnd = false
    private var endCharPosition: Int = 0
    private var startCharPosition: Int = 0
    private var hasSelection = false
    private var isRemoveSelection = false
    var onShow: (Float, Float) -> Unit = { _: Float, _: Float -> }
    var onHide: () -> Unit = {}

    //region 行操作
    private fun getLineText(line: Int): CharSequence {
        val start = textView.layout.getLineStart(line)
        val end = textView.layout.getLineEnd(line)
        return textView.text.subSequence(start, end)
    }

    //endregion

    //region 段落操作
    private fun getParagraphStart(line: Int): Int {
        var lineNumber = 0
        for (i in line - 1 downTo 0) {
            val text = getLineText(i)
            val idx = text.indexOf("\n")
            if (idx != -1) {
                lineNumber = i + 1
                break
            }
        }
        return lineNumber
    }

    private fun getParagraphEnd(line: Int): Int {
        var lineNumber = textView.lineCount - 1
        for (i in line until textView.lineCount) {
            val text = getLineText(i)
            val idx = text.indexOf("\n")
            if (idx != -1) {
                lineNumber = i
                break
            }
        }
        return lineNumber
    }

    private fun getParagraph(): Pair<Int, Int> {
        val currentLine = textView.layout.getLineForVertical(y.toInt())
        val startLine = getParagraphStart(currentLine)
        val endLine = getParagraphEnd(currentLine)
        val startPosition = textView.layout.getLineStart(startLine)
        val endPosition = textView.layout.getLineEnd(endLine)
        return Pair(startPosition, endPosition)
    }

    //endregion

    fun attachTo() {
        textView.doOnPreDraw { }
        textView.setOnTouchListener { _, event ->
            if (onTouchSelection(event)) {
                true
            } else if (onTouchClick(event)) {
                true
            } else {
                onTouchParent(event)
            }
        }
        textView.setOnLongClickListener {
            hasSelection = true
            isLongPress = true
            val (start, end) = getParagraph()
            startCharPosition = start
            endCharPosition = end
            setSelection()
            Log.i("测试长安吧", "setOnLongClickListener")
            true
        }
    }

    private fun setSelection() {

        if (hasSelection) {
            spannable.clear()
            spannable.clearSpans()
            spannable.append(textView.text)
            if (startCharPosition > endCharPosition) {
                startCharPosition = endCharPosition.also { endCharPosition = startCharPosition }
                dragSelectionStart = dragSelectionEnd.also { dragSelectionEnd = dragSelectionStart }
            }
            spannable.setSpan(
                bgSpan,
                startCharPosition,
                endCharPosition,
                Spanned.SPAN_INCLUSIVE_INCLUSIVE
            )
            textView.setText(spannable, TextView.BufferType.SPANNABLE)
        }
    }

    private fun removeSelection() {
        spannable.clearSpans()
        textView.setText(spannable, TextView.BufferType.SPANNABLE)
        hasSelection = false
        dragSelectionStart = false
        dragSelectionEnd = false
        isRemoveSelection = true
        Log.i("测试长安吧", "removeSelection")
        onHide()
    }

    private fun onTouchSelection(event: MotionEvent): Boolean {
        Log.i("测试长安吧", "onTouchSelection__${event.action}")
        x = event.x
        y = event.y
        if (isLongPress) {
            if (event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL) {
                isLongPress = false
            }
            return true
        }
        if (isRemoveSelection) {
            if (event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL) {
                isRemoveSelection = false
            }
            return true
        }
        if (hasSelection) {
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    dragSelectionStart = dragStartRectF.contains(x, y)
                    dragSelectionEnd = dragEndRectF.contains(x, y)
                    if (!dragSelectionStart && !dragSelectionEnd) removeSelection()
                }

                MotionEvent.ACTION_MOVE -> {
                    val position = textView.getOffsetForPosition(x, y - offsetY)
                    if (dragSelectionStart) startCharPosition = position
                    if (dragSelectionEnd) endCharPosition = position
                    setSelection()
                }

                MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                    dragSelectionEnd = false
                    dragSelectionStart = false
                }
            }
            return true
        }
        return false
    }

    private fun onTouchClick(event: MotionEvent): Boolean {
        return when (event.action) {
            MotionEvent.ACTION_UP -> textView.performClick()
            else -> false
        }
    }

    private fun onTouchParent(event: MotionEvent): Boolean {
        val parent = textView.parent
        if (parent is View) {
            parent.onTouchEvent(event)
        }
        return false
    }


    //region 背景样式
    inner class BgSpan(
        private val width: Float = 5f,
        private val radius: Float = 12f
    ) : LineBackgroundSpan {

        private val bgPaint = Paint().apply {
            color = Color.argb(128, 0, 0, 0)
        }
        private val cursorPaint = Paint().apply {
            color = Color.argb(255, 0, 0, 0)
            style = Paint.Style.FILL
            isAntiAlias = true
        }
        private val shadowPaint = Paint().apply {
            isAntiAlias = true
            color = Color.argb(191, 0, 0, 0)
            style = Paint.Style.FILL
            val shadowColor = Color.argb(128, 0, 0, 0)
            setShadowLayer(radius, 10f, 10f, shadowColor)
        }
        private val drawStartRectF = RectF()
        private var lineSpacing = 0f
        private var lineHeight: Float = 0f
        private val startId = R.drawable.ic_finger_black_24dp
        private var startDrawable: Drawable? = null
        private val endId = R.drawable.ic_finger_black_24dp
        private var endDrawable: Drawable? = null

        init {
            startDrawable = AppCompatResources.getDrawable(textView.context, startId)
            endDrawable = AppCompatResources.getDrawable(textView.context, endId)
        }

        private fun setColor() {
            val drawable = textView.background
            if (drawable is ColorDrawable) {
                bgPaint.color = changeColor(drawable.color, brightness = 0.9f, isRatio = true)
                cursorPaint.color = changeColor(drawable.color, brightness = 0.5f, isRatio = true)
                shadowPaint.color = changeColor(drawable.color, brightness = 0.5f, isRatio = true)
            }
        }

        override fun drawBackground(
            c: Canvas,
            p: Paint,
            left: Int,
            right: Int,
            top: Int,
            baseline: Int,
            bottom: Int,
            text: CharSequence,
            start: Int,
            end: Int,
            lineNumber: Int
        ) {
            val range = (start..end)
            val isStartLine = range.contains(startCharPosition)
            val isEndLine = range.contains(endCharPosition)

            val line = text.subSequence(start, end).toString()
            setColor()

            var l = left.toFloat()
            var nwsf = line.indexOfFirst { !it.isWhitespace() }
            if (isStartLine) {
                nwsf = maxOf(startCharPosition - start, nwsf)
                l += p.measureText(line.substring(0, nwsf))
            }
            var r = right.toFloat()
            var nwsl = line.indexOfLast { !it.isWhitespace() }
            if (isEndLine) {
                nwsl = minOf(endCharPosition - start, nwsl + 1)
                r = l + p.measureText(line.substring(nwsf, nwsl))
            }

            val metrics = p.fontMetrics
            val t = baseline + metrics.top
            val b = baseline + metrics.bottom
            c.drawRect(l, t, r, b, bgPaint)
            if (isStartLine) {
                drawStartRectF.set(l, t, r, b)
            }
            if (isEndLine) {
                lineSpacing = t - top + bottom - b
                lineHeight = textView.lineHeight.toFloat()

                drawEnd(c, p, l, t, r, b)
                val (l, t, r, b) = drawStartRectF
                drawStart(c, p, l, t, r, b)
            }
        }


        private fun drawStart(c: Canvas, p: Paint, l: Float, t: Float, r: Float, b: Float) {
            c.drawRect(l - width, t, l, b, cursorPaint)
            val cx = l - width / 2f
            val cy = t - radius
            c.drawCircle(cx, cy, radius, cursorPaint)
            drawStartController(c, RectF(l, t, r, b))
        }

        private fun drawStartController(c: Canvas, lineRectF: RectF) {
            val (l, t, r, b) = lineRectF
            val h = lineRectF.height()
            val ml = l - h / 2
            val mr = l + h / 2
            val mt = b
            val mb = b + lineHeight
            dragStartRectF = RectF(ml, mt, mr, mb)
            c.drawOval(dragStartRectF, shadowPaint)
            startDrawable?.apply {
                bounds = dragStartRectF.toRect()
                draw(c)
            }
            offsetY = dragStartRectF.centerY() - lineRectF.centerY()
        }

        private fun drawEnd(c: Canvas, p: Paint, l: Float, t: Float, r: Float, b: Float) {
            c.drawRect(r, t, r + width, b, cursorPaint)
            val cx = r + width / 2f
            val cy = t - radius
            c.drawCircle(cx, cy, radius, cursorPaint)
            drawEndController(c, RectF(l, t, r, b))
        }

        private fun drawEndController(c: Canvas, lineRectF: RectF) {
            val (l, t, r, b) = lineRectF
            val h = lineRectF.height()
            val ml = r - h / 2
            val mr = r + h / 2
            val mt = b
            val mb = b + lineHeight
            dragEndRectF = RectF(ml, mt, mr, mb)
            c.drawOval(dragEndRectF, shadowPaint)
            endDrawable?.apply {
                bounds = dragEndRectF.toRect()
                draw(c)
            }
            offsetY = dragEndRectF.centerY() - lineRectF.centerY()
            onShow(r, mb)
        }
    }

    //endregion
}